package com.exocr;

import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;

import com.baidu.ocr.sdk.OnResultListener;
import com.baidu.ocr.sdk.exception.OCRError;
import com.baidu.ocr.sdk.exception.SDKError;
import com.baidu.ocr.sdk.model.OcrRequestParams;
import com.baidu.ocr.sdk.model.OcrResponseResult;
import com.baidu.ocr.sdk.model.RequestParams;
import com.baidu.ocr.sdk.utils.HttpsClient;
import com.baidu.ocr.sdk.utils.OcrResultParser;
import com.baidu.ocr.sdk.utils.Parser;

import org.json.JSONObject;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import com.exocr.utills.Base64Util;
import com.exocr.utills.FileUtil;
import com.exocr.utills.HexUtil;
import com.exocr.utills.HttpUtil;

public class XFOCRHelper {
//    private static final String ocrRoot = "https://webapi.xfyun.cn/v1/service/v1/ocr/";
//    private static final String ocr2Root_ocr = "https://tyocr.xfyun.cn/v2/";
//    private static final String ocr2Root_itr = "https://rest-api.xfyun.cn/v2/";

//    // 应用APPID(必须为webapi类型应用,并开通手写文字识别服务,参考帖子如何创建一个webapi应用：http://bbs.xfyun.cn/forum.php?mod=viewthread&tid=36481)
//    public static String APPID = "595359f4";
//    // 接口密钥(webapi类型应用开通手写文字识别后，控制台--我的应用---手写文字识别---相应服务的apikey)
//    public static String API_KEY = "ebe7a44e6912916d59ad2cd9d267d5df";
//    public static String API_SECRET ="2c582e8f50aacf3d78ba5fa329409c75";
//
//    public static String API_KEY_V2 = "9a2682375768e0eff2ec95c8ce1ed881";
    //借用了百度识别sdk的 网络接口
    public static void doOCR(String orcFullUrl,OCRTypes ocrType, String filePath,final OCRCallBack callBack){
        try {
            if (ocrType==null||TextUtils.isEmpty(filePath)){
                if (callBack!=null){
                    callBack.result(null);
                }
                return;
            }
            if (!ocrType.isKeyAvailable()){
                if (callBack!=null){
                    callBack.result(new Exception("缺少必要的apikey等信息"));
                }
                return;
            }
            OnResultListener ocrResponseResultListener= new OnResultListener<OcrResponseResult>() {
                public void onResult(OcrResponseResult result) {
                    if (callBack != null) {
                        callBack.result(result);
                    }
                }

                public void onError(OCRError error) {
                    if (callBack != null) {
                        callBack.result(error);
                    }
                }
            };
            String ocrBranch=ocrType.getOcrBranch();

            Map<String, String> header =new HashMap<>();
            boolean isv2=isV2Root(ocrBranch);
            Object httpBody=null;

            // 读取图像文件，转二进制数组，然后Base64编码
            long flen=new File(filePath).length();
            float imgsizeMB=flen/(1024*1024.0f);
            if (imgsizeMB>3.0f){
                String ssize=String.format("%.2f",imgsizeMB);
                Log.i("ocr","img-error:不得超过3MB"+"("+ssize+")");
            }
            byte[] imageByteArray = FileUtil.read2ByteArray(filePath);
            boolean usepost2=true;
            String ocrUrl=ocrType.getUrlRoot()+ocrType.getOcrFunc();
            if (isv2){
                String imageBase64 = new String(Base64Util.getEncoder().encodeToString(imageByteArray));
//                if (OCRTypes.OCRXFBranch.type_ocr.equals(ocrBranch)){
//                    ocrUrl=ocr2Root_ocr+ocrBranch;
//                }else if (OCRTypes.OCRXFBranch.type_itr.equals(ocrBranch)||
//                        OCRTypes.OCRXFBranch.type_itr_et.equals(ocrBranch)){
//                    ocrUrl=ocr2Root_itr+OCRTypes.OCRXFBranch.type_itr;
//                }else{
//
//                }
                JSONObject jsonBody=getv2body(ocrType);
                JSONObject data = new JSONObject();
                data.put("image", imageBase64);
                jsonBody.put("data", data);
                httpBody=jsonBody.toString();
                header = getv2Header(ocrUrl,(String)httpBody,ocrType);
            }else{
//                ocrUrl=ocrRoot+ocrBranch;
                String imageBase64 = new String(Base64.encode(imageByteArray, Base64.NO_WRAP), "UTF-8");
                Map p=getv1Params(ocrBranch);
                header = getv1Header(ocrType,p);
                if (usepost2){
                    httpBody="image="+imageBase64;
                }else{
                    OcrRequestParams params = new OcrRequestParams();
                    params.putParam("image", imageBase64);
                    httpBody=params;
                }
            }
            if (!TextUtils.isEmpty(orcFullUrl)){
                ocrUrl=orcFullUrl;
            }
            if (TextUtils.isEmpty(ocrUrl)){
                if (callBack != null) {
                    callBack.result(new Exception("ocr地址不可为空"));
                }
            }else{
                if (usepost2){
                    post2(ocrUrl,header,httpBody,callBack);
                }else{
                    Parser<OcrResponseResult> ocrResultParser = new OcrResultParser();
                    post(ocrUrl,header, httpBody, ocrResultParser,ocrResponseResultListener);
                }
            }
        }catch (Exception e){
            e.printStackTrace();
            if (callBack != null) {
                callBack.result(e);
            }
        }
    }
    public static Map<String,String> getv1Params(String ocrBranch) throws Exception {
        Map<String, String> params=new HashMap<>();
        params.put("engine_type",ocrBranch);
        if (OCRTypes.OCRXFBranch.type_handwriting.equals(ocrBranch)){///eee
//            params.put("language","en");
            params.put("language","cn|en");
            params.put("location","false");
            params.remove("engine_type");
        }else if (OCRTypes.OCRXFBranch.type_general.equals(ocrBranch)){
            params.put("language","cn|en");
//            params.put("location","true");
        }else if (OCRTypes.OCRXFBranch.type_recognize_document.equals(ocrBranch)){///eee
        }else if (OCRTypes.OCRXFBranch.type_business_card.equals(ocrBranch)){///eee
//            params.put("pic_required","1");
        }else if (OCRTypes.OCRXFBranch.type_idcard.equals(ocrBranch)){///eee
//            params.put("head_portrait","1");
//            params.put("crop_image","1");
//            params.put("id_number_image","1");
//            params.put("recognize_mode","0");
        }else if (OCRTypes.OCRXFBranch.type_bankcard.equals(ocrBranch)){///eee
//            params.put("card_number_image","1");
        }
        return params;
    }
    /**
     * 组装http请求头
     */
    public static Map<String, String> getv1Header(OCRTypes ocrType,Map<String,String> params) throws Exception {
        // 系统当前时间戳
        String X_CurTime = System.currentTimeMillis() / 1000L + "";
        // 业务参数
        String param ="";
        if (params!=null){
            JSONObject jsonObj=new JSONObject(params);
            param=jsonObj.toString();
        }
//        param = "{\"language\":\"en\""+",\"location\":\"false\"}";
        byte[] btData=param.getBytes("UTF-8");
        String X_Param = new String(Base64.encode(btData,Base64.NO_WRAP));
        // 接口密钥
        String apiKey = ocrType.getApiKey();//API_KEY;
        // 讯飞开放平台应用ID
        String X_Appid = ocrType.getAppid();//APPID;
        // 生成令牌
        String lp=apiKey + X_CurTime + X_Param;
        byte[] utfBts=getBytesUtf8(lp);
        byte[] blp=MessageDigest.getInstance("MD5").digest(utfBts);
        String X_CheckSum = HexUtil.encodeHexString(blp);

        Map<String, String> header = new HashMap<>();
        header.put("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
        header.put("X-Param", X_Param);
        header.put("X-CurTime", X_CurTime);
        header.put("X-CheckSum", X_CheckSum);
        header.put("X-Appid", X_Appid);
        return header;
    }

    public static Map<String, String> getv2Header(String httpUrl,String body,OCRTypes ocrType) throws Exception {
        Map<String, String> header = new HashMap<String, String>();
        URL url = new URL(httpUrl);
        //时间戳
        SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
        format.setTimeZone(TimeZone.getTimeZone("GMT"));
        Date dateD = new Date();
        String date = format.format(dateD);
        //System.out.println("【指尖文字识别 WebAPI date】\n" + date);

        //对body进行sha256签名,生成digest头部，POST请求必须对body验证
        String digestBase64 = "SHA-256=" + signBody(body);
        //System.out.println("【指尖文字识别 WebAPI digestBase64】\n" + digestBase64);

        //hmacsha256加密原始字符串
        StringBuilder builder = new StringBuilder("host: ").append(url.getHost()).append("\n").//
                append("date: ").append(date).append("\n").//
                append("POST ").append(url.getPath()).append(" HTTP/1.1").append("\n").//
                append("digest: ").append(digestBase64);
        //System.out.println("【指尖文字识别 WebAPI builder】\n" + builder);
        String sha = hmacsign(builder.toString(), ocrType.getApiSecretKey());//API_SECRET);
        //System.out.println("【指尖文字识别 WebAPI sha】\n" + sha);

        String apikey_v2=ocrType.getApiKey_V2();
        //组装authorization
        String authorization = String.format("api_key=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"", apikey_v2, "hmac-sha256", "host date request-line digest", sha);
        //System.out.println("【指尖文字识别 WebAPI authorization】\n" + authorization);

        header.put("Authorization", authorization);
        header.put("Content-Type", "application/json");
        header.put("Accept", "application/json,version=1.0");
        header.put("Host", url.getHost());
        header.put("Date", date);
        header.put("Digest", digestBase64);
        //System.out.println("【指尖文字识别 WebAPI header】\n" + header);
        return header;
    }
    public static JSONObject getv2body(OCRTypes ocrType) throws Exception {
        String ocrBranch=ocrType.getOcrBranch();
        JSONObject common = new JSONObject();
        //填充common
        common.put("app_id",ocrType.getAppid());//APPID);
        JSONObject business = new JSONObject();
        if (OCRTypes.OCRXFBranch.type_ocr.equals(ocrBranch)){
            //填充business
            business.put("ent", "fingerocr");
            business.put("mode", "finger+ocr");
            business.put("method", "dynamic");
            //business.addProperty("cut_w_scale", 5.0);
            //business.addProperty("cut_h_scale", 2);
            //business.addProperty("cut_shift", 1);
            business.put("resize_w", 1088);
            business.put("resize_h", 1632);
        }else if (OCRTypes.OCRXFBranch.type_itr.equals(ocrBranch)){
            business.put("ent", "math-arith");
            business.put("aue", "raw");
        }else if (OCRTypes.OCRXFBranch.type_itr_et.equals(ocrBranch)){
            business.put("ent", "teach-photo-print");
            business.put("aue", "raw");
        }
        //填充body
        JSONObject body = new JSONObject();
        body.put("common", common);
        body.put("business", business);
        return body;
    }
    public static boolean isV2Root(String ocrBranch){
        return  (OCRTypes.OCRXFBranch.type_itr_et.equals(ocrBranch)||
                OCRTypes.OCRXFBranch.type_itr.equals(ocrBranch)||
                OCRTypes.OCRXFBranch.type_ocr.equals(ocrBranch));
    }
    private static Handler handler=new Handler(Looper.getMainLooper());
    public static void post2(final String path,final Map<String, String> header,final  Object params,final OCRCallBack callBack){
        if (params instanceof String){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Object result=null;
                    try {
                        Map<String, Object> resultMap = HttpUtil.doPost2(path, header, (String)params);
                        if (resultMap.get("error")!=null){
                            result=resultMap.get("error");
                            if (result instanceof Exception){
                                Exception e=(Exception)result;
                                String msg=e.toString();
                                SDKError error = new SDKError(283504, msg!=null?msg:"Network error", e);
                                result=error;
                            }else if (result instanceof String){
                                SDKError error = new SDKError(283504, ""+result);
                                result=error;
                            }
                        }else{
                            result=resultMap;
                        }
                    }catch (Exception e){
                        String msg=e.toString();
                        SDKError error = new SDKError(283504, msg!=null?msg:"Network error", e);
                        result=error;
                    }
                    final Object resultT=result;
                    handler.post(new Runnable() {
                        public void run() {
                            if (callBack!=null){
                                callBack.result(resultT);
                            }
                        }
                    });
                }
            }).start();

        }
    }
    public static <T>  void post(String path,Map<String, String> header, Object params, final Parser<T> parser, final OnResultListener<T> listener) {
        HttpsClient cl = new HttpsClient();
        HttpsClient.RequestBody body = new HttpsClient.RequestBody();

        if (params instanceof RequestParams){
            body.setStrParams(((RequestParams)params).getStringParams());
            body.setFileParams(((RequestParams)params).getFileParams());
        }else if (params instanceof String){
            body.setBody((String)params);
        }
        HttpsClient.RequestInfo reqInfo = new HttpsClient.RequestInfo(path, body);
        if (header!=null){
            for(String key : header.keySet()){
                String value = header.get(key);
                reqInfo.setHeader(key,value);
                System.out.println(key+":"+value);
            }
        }
        reqInfo.build();
        cl.newCall(reqInfo).enqueue(new HttpsClient.Callback() {
            public void onFailure(final Throwable e) {
                handler.post(new Runnable() {
                    public void run() {
                        String msg=e!=null? e.toString():null;//https://tyocr.xfyun.cn/v2/ocr
                        SDKError error = new SDKError(283504, !TextUtils.isEmpty(msg)?msg:"Network error", e);
                        listener.onError(error);
                    }
                });
            }

            public void onResponse(String resultStr) {
                String responseString = resultStr;

                try {
                    final T result = parser.parse(responseString);
                    handler.post(new Runnable() {
                        public void run() {
                            listener.onResult(result);
                        }
                    });
                } catch (final OCRError var4) {
                    handler.post(new Runnable() {
                        public void run() {
                            listener.onError(var4);
                        }
                    });
                }
            }
        });
    }
    //////////////////////
    /////////////////////

    /**
     * 对body进行SHA-256加密
     */
    private static String signBody(String body) throws Exception {
        MessageDigest messageDigest;
        String encodestr = "";
        try {
            messageDigest = MessageDigest.getInstance("SHA-256");
            messageDigest.update(body.getBytes("UTF-8"));
            encodestr = Base64Util.getEncoder().encodeToString(messageDigest.digest());
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return encodestr;
    }

    /**
     * hmacsha256加密
     */
    private static String hmacsign(String signature, String apiSecret) throws Exception {
        Charset charset = Charset.forName("UTF-8");
        Mac mac = Mac.getInstance("hmacsha256");
        SecretKeySpec spec = new SecretKeySpec(apiSecret.getBytes(charset), "hmacsha256");
        mac.init(spec);
        byte[] hexDigits = mac.doFinal(signature.getBytes(charset));
        try {
          return Base64Util.getEncoder().encodeToString(hexDigits);
        }catch (Exception e){
            e.printStackTrace();
        }
        return "";

    }
    public static byte[] getBytesUtf8(String string)  throws Exception{
        return getBytesUnchecked(string, "UTF-8");
    }

    public static byte[] getBytesUnchecked(String string, String charsetName) throws Exception {
        if (string == null) {
            return null;
        } else {
            try {
                return string.getBytes(charsetName);
            } catch (UnsupportedEncodingException var3) {
                throw new Exception(charsetName, var3);
            }
        }
    }
}
